/*
 * CacheServer.cpp
 *
 *  Created on: 2013. 8. 5.
 *      Author: jjaehuny
 */

#include "CacheServer.h"
#include "Static.h"

#include <Network/Static.h>
#include <Logger/Static.h>

#include "../Proxy.h"
#include "../User.h"
#include "../Session.h"
#include "../Static.h"

#include "../NLogic/Item.h"
#include "../NLogic/Bag.h"
#include "../NLogic/Equip.h"
#include "../NLogic/Reward.h"
#include "../NLogic/BackEndServerInfo.h"
#include "../NLogic/Score.h"

#include "../Enum.h"

#include "../NState/Static.h"

#include "Static.h"

#include <assert.h>

namespace ProjectZ { namespace NProxy {

void
CacheServer::Msg::OnOpenAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	PROCESSLOG("[CACHE] OnOpen Server IP : %s PORT : %d", pSession->GetNetworkPeerInfo().ip.c_str(), pSession->GetNetworkPeerInfo().port);

	Session * pLocalSession = static_cast<Session *>(pSession);
	CacheServer * pServer = static_cast<CacheServer *>(pLocalSession->GetOwner());
	::ProjectZ::Static::Instance()._cacheServer[pServer->GetIdx()] = true;

	//CacheServer * pCacheServer = static_cast<CacheServer *>(pSession->GetOwner());
	//pCacheServer->OnDisconnect();
	//s_CacheTree[pCacheServer->_idx] = pCacheServer;
}

void
CacheServer::Msg::OnCloseAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	PROCESSLOG("[CACHE] OnClose Server IP : %s PORT : %d", pSession->GetNetworkPeerInfo().ip.c_str(), pSession->GetNetworkPeerInfo().port);
	//DEBUGLOG("TRACE [CACHE] OnClose Server IP : %s PORT : %d", pSession->GetNetworkPeerInfo().ip.c_str(), pSession->GetNetworkPeerInfo().port);

//	CacheServer * pCacheServer = static_cast<CacheServer *>(pSession);
//	s_CacheTree.erase(pCacheServer->_idx);

	Session * pLocalSession = static_cast<Session *>(pSession);
	CacheServer * pServer = static_cast<CacheServer *>(pLocalSession->GetOwner());
	pServer->OnDisconnect();
}

void
CacheServer::Msg::OnUserInfoAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::UserInfoAck ans;
	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		DEBUGLOG("[CACHE OnUserInfoAck] Not Found User");
		return;
	}

	// 유저 정보를 요청 했지만 실패가 발생한 경우...
	CacheResult result = ans.result;
	if (result != CACHE_SUCCESS)
	{
		DEBUGLOG("[CACHE OnUserInfoAck] NOT CACHE_SUCCESS");
		return;
	}

	// 유저 정보를 요청 요청한 유저가 존재하지 않는다..
	//User * pUser = s_UserTree[ans.seq];
	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		DEBUGLOG("[CACHE OnUserInfoAck] USER NOT FOUND");
		return;
	}


	// 데이터 저장
	DEBUGLOG("[CACHE OnUserInfoAck] DATA SAVE SUCCESS GID : %d", ans.seq);
	pUser->SetUserInfo(ans.stUserInfo);

	int nOpenCount = 0;
	for (int i = 0; i<8; ++i)
	{
		if (true == pUser->GetUserInfo().array_Slot[i].open && true == pUser->GetUserInfo().array_Slot[i].makeCharacter)
		{
			nOpenCount++;
		}
	}

	pUser->SetOpenCharacterCount(nOpenCount);

	if (nOpenCount <= 0)
	{
		// LOGIN OK 시점에 Guild LoginTime을 갱신한다
		NProxy::UpdateGuildLoginTime(pUser);

		// 로그인 이후 오픈된 모든 케릭터 정보를 로드 했으므로 여기서 상태를 변경한다.
		pUser->SetLogin();
		pUser->SetState(NState::Static::Instance().MAINFRIENDLIST());

		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		DEBUGLOG("[CHANNEL CS_REQ_LOGIN] ACK_OK");
		pAck->Write2(ACK_OK);
		* pAck << 0;
		pAck->MakePacket(CMD_SC_REQ_LOGIN);
		//Encrypt((unsigned char *)public_key, strlen(public_key), pAck);
		pUser->Send(pAck);

//		pUser->SetLogin();
		return;
	}

	for (int i = 0; i<8; ++i)
	{
		if (true == pUser->GetUserInfo().array_Slot[i].open && 0 != pUser->GetUserInfo().array_Slot[i].character_seq &&
			true == pUser->GetUserInfo().array_Slot[i].makeCharacter)
		{
			::ProjectZ::NProxy::CharacterInfoSyn(pUser, pUser->GetUserInfo().array_Slot[i].character_seq);
			//DEBUGLOG("TRACE character info syn char seq : %d GID : %d", pUser->GetUserInfo().array_Slot[i].character_seq, pUser->GetUserSeq());
		}
	}

	NLogic::Reward * reward = pUser->GetReward();
	reward->load_Reward();

	pUser->GetScore()->LoadMedal();

	DEBUGLOG("[CACHE OnUserInfoAck] OPEN CHARACTER COUNT : %d GID : %d", pUser->GetOpenCharacterCount(), pUser->GetUserSeq());
}

void
CacheServer::Msg::OnCharacterInfoAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::CharacterInfoAck ans;
	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	//DEBUGLOG("TRACE OnCharacterInfoAck seq : %d result : %d msg : %s", ans.seq, ans.result, ans.strError.c_str());

	// 유저 정보를 요청 요청한 유저가 존재하지 않는다..
	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		DEBUGLOG("User is null");
		return;
	}

	// 유저 정보를 요청 했지만 실패가 발생한 경우...
	CacheResult result = ans.result;
	if (result != CACHE_SUCCESS)
	{
		DEBUGLOG("ONCHARACTERINFOACK NOT CACHE_SUCCESS");
		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		pAck->MsgCat("%2d%d", ACK_DB_ERROR, ans.seq);
		pAck->MakePacket(CMD_SC_INPUTINFO);
		pUser->Send(pAck);

		return;
	}

	if (0 > ans.stCharacterInfo.slotindex || ans.stCharacterInfo.slotindex > 7)
	{
		DEBUGLOG("ERROR [CACHE OnUserInfoAck] slotindex : %d GID : %d", ans.stCharacterInfo.slotindex, pUser->GetUserSeq());

		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		DEBUGLOG("[CHANNEL OnCharacterInfoAck] CMD_SC_REQ_LOGIN ACK_UNKNOWN_ERROR : USER GID : %d", pUser->GetUserSeq());
		pAck->Write2(ACK_UNKNOWN_ERROR);
		* pAck << pUser->GetUserSeq();
		pAck->MakePacket(CMD_SC_REQ_LOGIN);
		pUser->Send(pAck);

		return;
	}

	// 데이터 저장
	pUser->SetCharacterInfo(ans.stCharacterInfo, ans.stCharacterInfo.slotindex);
	pUser->AddLoadCharacterCount();

	if (pUser->GetLoadCharacterCount() < pUser->GetOpenCharacterCount())
		return;

//	if (pUser->GetLoadCharacterCount() > pUser->GetOpenCharacterCount())
//	{
////		DEBUGLOG("TRACE over character count load : %d open : %d GID : %d slotindex : %d",
////			pUser->GetLoadCharacterCount(), pUser->GetOpenCharacterCount(),
////			pUser->GetUserSeq(), ans.stCharacterInfo.slotindex );
//
//		return;
//	}
	DEBUGLOG("[CACHE OnUserInfoAck] LOAD CHARACTER COUNT : %d", pUser->GetLoadCharacterCount());


	// 로그인 이후 오픈된 모든 케릭터 정보를 로드 했으므로 여기서 상태를 변경한다.
	pUser->SetCharacterInfoCache();

	if (false == NProxy::SetMainCharacterInfo(pUser))
	{
		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		DEBUGLOG("[CHANNEL OnCharacterInfoAck] CMD_SC_REQ_LOGIN ACK_UNKNOWN_ERROR : USER GID : %d", pUser->GetUserSeq());
		pAck->Write2(ACK_UNKNOWN_ERROR);
		* pAck << pUser->GetUserSeq();
		pAck->MakePacket(CMD_SC_REQ_LOGIN);
		pUser->Send(pAck);

		return;
	}

	NLogic::Bag * pBag = pUser->GetBag(BAG_TYPE_NORMAL);
	if (NULL == pBag)
	{
		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		DEBUGLOG("[CHANNEL OnCharacterInfoAck] CMD_SC_REQ_LOGIN ACK_UNKNOWN_ERROR : USER GID : %d", pUser->GetUserSeq());
		pAck->Write2(ACK_UNKNOWN_ERROR);
		* pAck << pUser->GetUserSeq();
		pAck->MakePacket(CMD_SC_REQ_LOGIN);
		pUser->Send(pAck);

		return;
	}

	if (false == pBag->ItemLoadFromDatabase_Asyn())
	{
		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		DEBUGLOG("[CHANNEL OnCharacterInfoAck] CMD_SC_REQ_LOGIN ACK_DB_ERROR : USER GID : %d", pUser->GetUserSeq());
		pAck->Write2(ACK_DB_ERROR);
		* pAck << pUser->GetUserSeq();
		pAck->MakePacket(CMD_SC_REQ_LOGIN);
		pUser->Send(pAck);
	}

#if 0
	pUser->SetLogin();
	pUser->SetState(NState::Static::Instance().MAINFRIENDLIST());

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	DEBUGLOG("[CHANNEL OnCharacterInfoAck] CMD_SC_REQ_LOGIN ACK_OK : USER GID : %d", pUser->GetUserSeq());
	pAck->Write2(ACK_OK);
	* pAck << pUser->GetUserSeq();
	pAck->MakePacket(CMD_SC_REQ_LOGIN);
	pUser->Send(pAck);
#endif



}

void
CacheServer::Msg::OnFlushCharacterInfoAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::FlushCharacterInfoAck ans;
	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	DEBUGLOG("OnFlushCharacterInfoAck seq : %d result : %d msg : %s", ans.seq, ans.result, ans.strError.c_str());

	// 유저 정보를 요청 했지만 실패가 발생한 경우...
	CacheResult result = ans.result;
	if (result != CACHE_SUCCESS)
	{
		DEBUGLOG("CACHEFAIL ONCHARACTERINFOACK NOT CACHE_SUCCESS GID : %d", ans.seq);

		// user find
		User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
		if (NULL == pUser)
		{
			//DEBUGLOG("User is null");
			DEBUGLOG("[CACHE] OnFlushCharacterInfoAck USER IS NULL GID : %d", ans.seq);
			return;
		}

		pUser->SetCacheFail();

		User::State * pState = pUser->GetState();
		if (NULL != pState)
		{
			DEBUGLOG("CACHEFAIL ONFLUSHCHARACTERINFOACK STATE : %s ENUMSTATE : %d GID : %d",
					pUser->GetState()->GetName().c_str(), pUser->GetEnumState(), ans.seq);
			pState->OnClose(pUser, true);
		}
		//pUser->CleanUp();

		return;
	}

	//DEBUGLOG("User main slot index : %d socialid : %s", pUser->GetUserInfo().main_slot_index, pUser->socialid);

	// message 전달....
}

void
CacheServer::Msg::OnFlushUserInfoAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::FlushUserInfoAck ans;
	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	DEBUGLOG("OnFlushUserInfoAck seq : %d result : %d msg : %s", ans.seq, ans.result, ans.strError.c_str());

	// 유저 정보를 요청 했지만 실패가 발생한 경우...
	CacheResult result = ans.result;
	if (result != CACHE_SUCCESS)
	{
		DEBUGLOG("CACHEFAIL OnFlushUserInfoAck NOT CACHE_SUCCESS");

		// user find
		User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
		if (NULL == pUser)
		{
			//DEBUGLOG("User is null");
			DEBUGLOG("[CACHE] OnFlushUserInfoAck USER IS NULL GID : %d", ans.seq);
			return;
		}

		pUser->SetCacheFail();
		User::State * pState = pUser->GetState();
		if (NULL != pState)
		{
			DEBUGLOG("CACHEFAIL ONFLUSHUSERINFOACK STATE : %s ENUMSTATE : %d GID : %d",
					pUser->GetState()->GetName().c_str(), pUser->GetEnumState(), ans.seq);
			pState->OnClose(pUser, true);
		}
		//pUser->CleanUp();

		return;
	}

	// user find
	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		//DEBUGLOG("[CACHE] OnFlushUserInfoAck User is null");
		return;
	}

	//DEBUGLOG("OnFlushUserInfoAck User main slot index : %d socialid : %s", pUser->GetUserInfo().main_slot_index, pUser->socialid);

	// message 전달....
}

void
CacheServer::Msg::OnRemoveUserAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::RemoveUserAck ans;
	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	DEBUGLOG("OnRemoveUserAck seq : %d result : %d msg : %s", ans.seq, ans.result, ans.strError.c_str());

	// 유저 정보를 요청 했지만 실패가 발생한 경우...
	CacheResult result = ans.result;
	if (result != CACHE_SUCCESS)
	{
		DEBUGLOG("ONREMOVEUSERACK NOT CACHE_SUCCESS");
		return;
	}

	// user find
	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		DEBUGLOG("[CACHE] OnRemoveUserAck User is null");
		return;
	}
}

void
CacheServer::Msg::OnNewCharacterInfoAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::NewCharacterInfoAck ans;
	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	DEBUGLOG("OnNewCharacterInfoAck seq : %d result : %d msg : %s", ans.seq, ans.result, ans.strError.c_str());

	// 유저 정보를 요청 요청한 유저가 존재하지 않는다..
	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		//DEBUGLOG("User is null");
		DEBUGLOG("[CACHE] OnNewCharacterInfoAck User is null");
		return;
	}

	// 유저 정보를 요청 했지만 실패가 발생한 경우...
	CacheResult result = ans.result;
	if (result != CACHE_SUCCESS)
	{
		DEBUGLOG("OnNewCharacterInfoAck NOT CACHE_SUCCESS");
		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		pAck->Write2(ACK_DB_ERROR);
		pAck->MakePacket(CMD_SC_SLOT_PLAYER_CREATE);
		pUser->Send(pAck);

		return;
	}

	if (0 > ans.stCharacterInfo.slotindex || ans.stCharacterInfo.slotindex > 7)
	{
		DEBUGLOG("ERROR OnNewCharacterInfoAck slotindex : %d GID : %d", ans.stCharacterInfo.slotindex, pUser->GetUserSeq());

		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		pAck->Write2(ACK_DB_ERROR);
		pAck->MakePacket(CMD_SC_SLOT_PLAYER_CREATE);
		pUser->Send(pAck);

		return;
	}

	// 데이터 저장
	pUser->SetCharacterInfo(ans.stCharacterInfo, ans.stCharacterInfo.slotindex);
	pUser->SetCharacterInfoCache();
	pUser->AddOpenCharacterCount();

	// 유저에게 기본 아이템을 제공한다....
	pUser->GiveBaseItem(ans.stCharacterInfo.slotindex);

	DEBUGLOG("OnNewCharacterInfoAck SUCCESS SEND CMD_SC_SLOT_PLAYER_CREATE");

	// 무기, 탈것, 페어리 정보를 한번 업데이트 하자....
	//ProjectZ::FlushCharacterInfoSyn(pUser, ans.stCharacterInfo.characterseq, true);

	// 로그인 이후 오픈된 모든 케릭터 정보를 로드 했으므로 여기서 상태를 변경한다.
	pUser->SetState(NState::Static::Instance().MAINFRIENDLIST());

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	pAck->Write2(ACK_OK);
	pAck->MakePacket(CMD_SC_SLOT_PLAYER_CREATE);
	pUser->Send(pAck);

	pUser->FlushNewCharacterInfo(ans.stCharacterInfo.slotindex);

	pUser->CacheBagInfo(false);
}

void
CacheServer::Msg::OnFlushUserSlotInfoAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::FlushUserSlotInfoAck ans;
	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	DEBUGLOG("OnFlushUserSlotInfoAck seq : %d result : %d msg : %s", ans.seq, ans.result, ans.strError.c_str());

	// 유저 정보를 요청 했지만 실패가 발생한 경우...
	CacheResult result = ans.result;
	if (result != CACHE_SUCCESS)
	{
		DEBUGLOG("[CACHE OnFlushUserSlotInfoAck] CACHE_FAIL");
		return;
	}

	// 유저 정보를 요청 요청한 유저가 존재하지 않는다..
	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		DEBUGLOG("[CACHE] OnFlushUserSlotInfoAck User is null");
		return;
	}

	::ProjectZ::NProxy::NewCharacterInfoSyn(pUser, pUser->GetCharacterInfoFromIndex(ans.slotIndex).characterseq);
}



CacheServer::CacheServer() : _idx(0), _bConnect(false)
{
	//_pSession = new ::Dispatcher::Session;
	_pSession = new Session;
	_pSession->SetState(new State());

	_pSession->SetOwner(this);
}

CacheServer::~CacheServer() {
	delete _pSession->GetState();
	delete _pSession;
}

bool
CacheServer::Connect(int idx, std::string strIP, int port)
{
	//::Network::Session::Info clsInfo;
	_clsInfo.eType = ::Network::Session::Info::SERVER;
	_clsInfo.ip = strIP.c_str();
	_clsInfo.port = port;
	_clsInfo.keepAliveTime = 3000 * 10;
	_idx = idx;

	bool bConnect = _pSession->Connect(_clsInfo);

	if (true == bConnect)
		_bConnect = true;

	return bConnect;
}

void
CacheServer::OnDisconnect()
{
	auto tree = GetUserTree();
	auto itr = tree.begin();
	for ( ; itr != tree.end(); ++itr)
	{
		User * pUser = itr->second;
		if (NULL == pUser)
			continue;

		::ProjectZ::Session * pSession = pUser->GetSession();
		if (NULL == pSession)
			continue;

		NLogic::BackEndServerInfo * pBackEndServerInfo = pUser->GetBackEndServerInfo();
		if (NULL == pBackEndServerInfo)
			continue;

		if (pBackEndServerInfo->GetData()._cacheServerIndex != this->_idx)
			continue;

		//pSession->Close();

		User::State * pState = pUser->GetState();
		if (NULL != pState)
			pState->OnClose(pUser, true);

		//pUser->CleanUp();
	}

	_bConnect = false;
	this->setTime();
}

bool
CacheServer::OnTime(int id, void* param)
{
	_pSession = new Session;
	_pSession->SetState(new State());
	_pSession->SetOwner(this);

	bool bConnect = _pSession->Connect(_clsInfo);

	if (true == bConnect)
		_bConnect = true;
	else
	{
		// 타이머 재 등록...
		this->setTime();
	}

	return false;
}

void
CacheServer::setTime()
{
	// 15 초 타이머 진행....
	::Dispatcher::Timer::Argument clsArgument(1000 * 15, 0, NULL, this);
	::Dispatcher::GETTIMER()->SetTimer(clsArgument);
}

} /*NProxy*/ } /*ProjectZ*/

